﻿#pragma once

#include "../MirObjects/MapObject.h"
#include "../MessageQueue.h"
#include "RespawnTimer.h"
#include "../MirNetwork/MirStatusConnection.h"
#include "../MirNetwork/MirConnection.h"
#include "../MirDatabase/MapInfo.h"
#include "../../Shared/Data/ItemData.h"
#include "../MirDatabase/MonsterInfo.h"
#include "../MirDatabase/MagicInfo.h"
#include "../MirDatabase/NPCInfo.h"
#include "../MirDatabase/DragonInfo.h"
#include "../MirDatabase/QuestInfo.h"
#include "../MirDatabase/RecipeInfo.h"
#include "../MirDatabase/BuffInfo.h"
#include "../MirDatabase/AccountInfo.h"
#include "../MirDatabase/CharacterInfo.h"
#include "../MirDatabase/AuctionInfo.h"
#include "../MirObjects/GuildObject.h"
#include "Map.h"
#include "../MirDatabase/SafeZoneInfo.h"
#include "../MirObjects/PlayerObject.h"
#include "../MirObjects/SpellObject.h"
#include "../MirObjects/NPCObject.h"
#include "../../Shared/Enums.h"
#include "../MirObjects/NPC/NPCScript.h"
#include "../MirDatabase/ConquestInfo.h"
#include "../MirObjects/ConquestObject.h"
#include "../Settings.h"
#include "Dragon.h"
#include "../../Shared/Data/SharedData.h"
#include "../Utils/HttpServer.h"
#include "../../Shared/Packet.h"
#include "../../Shared/ClientPackets.h"
#include "../MirDatabase/QuestProgressInfo.h"
#include "../../Shared/Data/GuildData.h"
#include <string>
#include <unordered_map>
#include <vector>
#include <list>
#include <algorithm>
#include <limits>
#include <stdexcept>
#include <any>
#include <mutex>
#include "../../stringhelper.h"
#include "../../tangible_filesystem.h"

using namespace Server::Library::Utils;
using namespace Server::MirDatabase;
using namespace Server::MirNetwork;
using namespace Server::MirObjects;
namespace S = ServerPackets;

namespace Server::MirEnvir
{
    class MobThread
    {
    public:
        int Id = 0;
        long long LastRunTime = 0;
        long long StartTime = 0;
        long long EndTime = 0;
        std::list<MapObject*> ObjectsList = std::list<MapObject*>();
        LinkedListNode<MapObject*> *_current = nullptr;
        bool Stop = false;
        virtual ~MobThread()
        {
            delete _current;
        }

    };

    class RandomProvider
    {
    private:
        static int seed;
        static ThreadLocal<Random*> *const RandomWrapper;

    public:
        static Random *GetThreadRandom();

        int Next();
        int Next(int maxValue);
        int Next(int minValue, int maxValue);
    };

    class Envir
    {
    private:
        static Envir *Main;
        static Envir *Edit;
        long long Time = 0;
        bool Running = false;

    public:
        virtual ~Envir()
        {
            delete Stopwatch;
            delete RespawnTick;
            delete Random;
            delete _thread;
            delete _listener;
            delete _StatusPort;
            delete DragonInfo;
            delete DragonSystem;
            delete DefaultNPC;
            delete MonsterNPC;
            delete RobotNPC;
        }

        static Envir *getMain();

        static Envir *getEdit();

    protected:
        static Server::MessageQueue *getMessageQueue();

    public:
        static std::mutex AccountLock;
        static std::mutex LoadLock;

        static constexpr int MinVersion = 60;
        static constexpr int Version = 87;
        static constexpr int CustomVersion = 0;
        static const std::string DatabasePath;
        static const std::string AccountPath;
        static const std::string BackUpPath;
        bool ResetGS = false;

    private:
        static Regex *AccountIDReg;
        static Regex *PasswordReg;
        static Regex *EMailReg;
        static Regex *CharacterReg;

    public:
        static int LoadVersion;
        static int LoadCustomVersion;

    private:
        const DateTime _startTime = DateTime::Now;
    public:
        Stopwatch *const Stopwatch = Stopwatch->StartNew();

        long long getTime() const;
        void setTime(long long value);
        RespawnTimer *RespawnTick = new RespawnTimer();

    private:
        static std::vector<std::string> DisabledCharNames;
        static std::vector<std::string> LineMessages;

    public:
        DateTime getNow() const;

        bool getRunning() const;
        void setRunning(bool value);


    private:
        static unsigned int _objectID;
    public:
        unsigned int getObjectID() const;

        static int _playerCount;
        int getPlayerCount() const;

        RandomProvider *Random = new RandomProvider();


    private:
        Thread *_thread;
        TcpListener *_listener;
        bool StatusPortEnabled = true;
    public:
        std::vector<MirStatusConnection*> StatusConnections = std::vector<MirStatusConnection*>();
    private:
        TcpListener *_StatusPort;
        int _sessionID = 0;
    public:
        std::vector<MirConnection*> Connections = std::vector<MirConnection*>();


        //Server DB
        int MapIndex = 0, ItemIndex = 0, MonsterIndex = 0, NPCIndex = 0, QuestIndex = 0, GameshopIndex = 0, ConquestIndex = 0, RespawnIndex = 0, ScriptIndex = 0;
        std::vector<MapInfo*> MapInfoList = std::vector<MapInfo*>();
        std::vector<ItemInfo*> ItemInfoList = std::vector<ItemInfo*>();
        std::vector<MonsterInfo*> MonsterInfoList = std::vector<MonsterInfo*>();
        std::vector<MagicInfo*> MagicInfoList = std::vector<MagicInfo*>();
        std::vector<NPCInfo*> NPCInfoList = std::vector<NPCInfo*>();
        DragonInfo *DragonInfo = new DragonInfo();
        std::vector<QuestInfo*> QuestInfoList = std::vector<QuestInfo*>();
        std::vector<GameShopItem*> GameShopList = std::vector<GameShopItem*>();
        std::vector<RecipeInfo*> RecipeInfoList = std::vector<RecipeInfo*>();
        std::vector<BuffInfo*> BuffInfoList = std::vector<BuffInfo*>();
        std::unordered_map<int, int> GameshopLog = std::unordered_map<int, int>();

        //User DB
        int NextAccountID = 0, NextCharacterID = 0;
        unsigned long long NextUserItemID = 0, NextAuctionID = 0, NextMailID = 0, NextRecipeID = 0;
        std::vector<AccountInfo*> AccountList = std::vector<AccountInfo*>();
        std::vector<CharacterInfo*> CharacterList = std::vector<CharacterInfo*>();
        std::list<AuctionInfo*> Auctions = std::list<AuctionInfo*>();
        int GuildCount = 0, NextGuildID = 0;
        std::vector<GuildObject*> GuildList = std::vector<GuildObject*>();


        //Live Info
        bool Saving = false;
        std::vector<Map*> MapList = std::vector<Map*>();
        std::vector<SafeZoneInfo*> StartPoints = std::vector<SafeZoneInfo*>();
        std::vector<ItemInfo*> StartItems = std::vector<ItemInfo*>();

        std::vector<PlayerObject*> Players = std::vector<PlayerObject*>();
        std::vector<SpellObject*> Spells = std::vector<SpellObject*>();
        std::vector<NPCObject*> NPCs = std::vector<NPCObject*>();

        LightSetting Lights = static_cast<LightSetting>(0);
        std::list<MapObject*> Objects = std::list<MapObject*>();
        std::unordered_map<int, NPCScript*> Scripts = std::unordered_map<int, NPCScript*>();


        std::vector<ConquestInfo*> ConquestInfos = std::vector<ConquestInfo*>();
        std::vector<ConquestObject*> Conquests = std::vector<ConquestObject*>();



        //multithread vars
    private:
        std::mutex _locker;
    public:
        std::vector<MobThread*> MobThreads = std::vector<MobThread*>(Settings::ThreadLimit);
    private:
        const std::vector<Thread*> MobThreading = std::vector<Thread*>(Settings::ThreadLimit);
    public:
        int SpawnMultiplier = 1; //set this to 2 if you want double spawns (warning this can easily lag your server far beyond what you imagine)

        std::vector<std::string> CustomCommands = std::vector<std::string>();

        Dragon *DragonSystem;
        NPCScript *DefaultNPC, *MonsterNPC, *RobotNPC;

        std::vector<DropInfo*> FishingDrops = std::vector<DropInfo*>();
        std::vector<DropInfo*> AwakeningDrops = std::vector<DropInfo*>();

        std::vector<DropInfo*> StrongboxDrops = std::vector<DropInfo*>();
        std::vector<DropInfo*> BlackstoneDrops = std::vector<DropInfo*>();

        std::vector<GuildAtWar*> GuildsAtWar = std::vector<GuildAtWar*>();
        std::vector<MapRespawn*> SavedSpawns = std::vector<MapRespawn*>();

        std::vector<RankCharacterInfo*> RankTop = std::vector<RankCharacterInfo*>();
        std::vector<std::vector<RankCharacterInfo*>> RankClass = std::vector<std::vector<RankCharacterInfo*>>(5);
        std::vector<int> RankBottomLevel = std::vector<int>(6);
    private:
        static HttpServer *http;
    private:
        class StaticConstructor
        {
        public:
            StaticConstructor();
        };

    private:
        static Envir::StaticConstructor staticConstructor;


    public:
        static int LastCount;
        static int LastRealCount;
        static long long LastRunTime;
        int MonsterCount = 0;

    private:
        long long warTime = 0, guildTime = 0, conquestTime = 0, rentalItemsTime = 0, auctionTime = 0, spawnTime = 0, robotTime = 0;
        int dailyTime = DateTime::Now.Day;

        bool MagicExists(Spell spell);

        void UpdateMagicInfo();

        void FillMagicInfoList();

        std::string CanStartEnvir();

        void WorkLoop();

        void ThreadLoop(MobThread *Info);

        void AdjustLights();

    public:
        void Process();

    private:
        void ProcessAuction();

    public:
        void Broadcast(Packet *p);

        void RequiresBaseStatUpdate();

        void SaveDB();
        void SaveAccounts();

    private:
        void SaveAccounts(Stream *stream);

        void SaveGuilds(bool forced = false);
        void EndSaveGuildsAsync(IAsyncResult *result);

        void SaveGoods(bool forced = false);
        void EndSaveGoodsAsync(IAsyncResult *result);

        void SaveConquests(bool forced = false);
        void EndSaveConquestsAsync(IAsyncResult *result);

    public:
        void BeginSaveAccounts();
    private:
        void EndSaveAccounts(IAsyncResult *result);

    public:
        bool LoadDB();

        void LoadAccounts();

        void LoadGuilds();

        void LoadFishingDrops();

        void LoadAwakeningMaterials();

        void LoadStrongBoxDrops();

        void LoadBlackStoneDrops();

        void LoadConquests();

        void LoadDisabledChars();

        void LoadLineMessages();

    private:
        bool BindCharacter(AuctionInfo *auction);

    public:
        void Start();
        void Stop();

        void Reboot();

    private:
        void StartEnvir();
        void StartNetwork();

        void StopEnvir();
        void StopNetwork();

        void CleanUp();

        void Connection(IAsyncResult *result);

        void StatusConnection(IAsyncResult *result);

    public:
        void NewAccount(ClientPackets::NewAccount *p, MirConnection *c);

        int HTTPNewAccount(ClientPackets::NewAccount *p, const std::string &ip);

        void ChangePassword(ClientPackets::ChangePassword *p, MirConnection *c);
        void Login(ClientPackets::Login *p, MirConnection *c);

        int HTTPLogin(const std::string &AccountID, const std::string &Password);

        void NewCharacter(ClientPackets::NewCharacter *p, MirConnection *c, bool IsGm);

        bool AccountExists(const std::string &accountID);
        bool CharacterExists(const std::string &name);

    private:
        AccountInfo *GetAccount(const std::string &accountID);
    public:
        std::vector<AccountInfo*> MatchAccounts(const std::string &accountID, bool match = false);

        std::vector<AccountInfo*> MatchAccountsByPlayer(const std::string &playerName, bool match = false);

        void CreateAccountInfo();
        void CreateMapInfo();
        void CreateItemInfo(ItemType type = ItemType::Nothing);
        void CreateMonsterInfo();
        void CreateNPCInfo();
        void CreateQuestInfo();

        void AddToGameShop(ItemInfo *Info);

        void Remove(MapInfo *info);
        void Remove(ItemInfo *info);
        void Remove(MonsterInfo *info);
        void Remove(NPCInfo *info);
        void Remove(QuestInfo *info);

        void Remove(GameShopItem *info);

        UserItem *CreateFreshItem(ItemInfo *info);
        UserItem *CreateDropItem(int index);
        UserItem *CreateDropItem(ItemInfo *info);

        UserItem *CreateShopItem(ItemInfo *info, unsigned long long id);

        void UpdateItemExpiry(UserItem *item);

        void UpgradeItem(UserItem *item);

        int RandomomRange(int count, int rate);
        bool BindItem(UserItem *item);

        bool BindGameShop(GameShopItem *item, bool editEnvir = true);

        bool BindSlotItems(UserItem *item);

        bool BindQuest(QuestProgressInfo *quest);

        Map *GetMap(int index);

        Map *GetMapByNameAndInstance(const std::string &name, int instanceValue = 0);

        MonsterInfo *GetMonsterInfo(int index);

        MonsterInfo *GetMonsterInfo(int ai, int effect = -1);

        NPCObject *GetNPC(const std::string &name);

        MonsterInfo *GetMonsterInfo(const std::string &name, bool Strict = false);
        PlayerObject *GetPlayer(const std::string &name);
        PlayerObject *GetPlayer(unsigned int PlayerId);
        CharacterInfo *GetCharacterInfo(const std::string &name);

        CharacterInfo *GetCharacterInfo(int index);

        ItemInfo *GetItemInfo(int index);
        ItemInfo *GetItemInfo(const std::string &name);
        QuestInfo *GetQuestInfo(int index);

        ItemInfo *GetBook(short Skill);

        void MessageAccount(AccountInfo *account, const std::string &message, ChatType type);


        void MailCharacter(CharacterInfo *info, UserItem *item = nullptr, unsigned int gold = 0, int reason = 0, const std::string &customMessage = "");

        GuildObject *GetGuild(const std::string &name);
        GuildObject *GetGuild(int index);

        void ProcessNewDay();

    private:
        void ProcessRentedItems();

    public:
        bool ReturnRentalItem(UserItem *rentedItem, const std::string &ownerName, CharacterInfo *rentingCharacterInfo, bool removeNow = true);

    private:
        void ClearDailyQuests(CharacterInfo *info);

    public:
        GuildBuffInfo *FindGuildBuffInfo(int Id);

        void ClearGameshopLog();

    private:
        const int RankCount = 100;

    public:
        int InsertRank(std::vector<RankCharacterInfo*> &Ranking, RankCharacterInfo *NewRank);

        bool TryAddRank(std::vector<RankCharacterInfo*> &Ranking, CharacterInfo *info, unsigned char type);

        int FindRank(std::vector<RankCharacterInfo*> &Ranking, CharacterInfo *info, unsigned char type);

        bool UpdateRank(std::vector<RankCharacterInfo*> &Ranking, CharacterInfo *info, unsigned char type);

        void SetNewRank(RankCharacterInfo *Rank, int Index, unsigned char type);

        void RemoveRank(CharacterInfo *info);

        void CheckRankUpdate(CharacterInfo *info);


        void ReloadNPCs();

        void ReloadDrops();
    };
}
